package lumis.portal.servlet;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

/**
 * Response wrapper used to capture HTML body for post-processing.
 *
 * @version $Revision: 13093 $ $Date: 2011-05-28 18:40:18 -0300 (Sat, 28 May 2011) $
 * @since 5.0.0
 */
public class BufferedHttpServletResponse extends HttpServletResponseWrapper
{
	/**
	 * Output stream used to store response content for future formatting.
	 *
	 * @version $Revision: 13093 $ $Date: 2011-05-28 18:40:18 -0300 (Sat, 28 May 2011) $
	 * @since 5.0.0
	 */
	private class BufferedOutputStream extends ServletOutputStream 
	{
		private ByteArrayOutputStream outputStream = new ByteArrayOutputStream(30000);
		
		@Override
		public void write(int b) throws IOException 
		{
			outputStream.write(b);
		}
		
		public ByteArrayOutputStream getOutputStream()
		{
			return outputStream;
		}
		
		/**
		 * Resets the data store in this stream, so that all currently 
		 * accumulated output is discarded. 
		 * The output stream can be used again. 
		 * @since 6.0.0
		 */
		public void reset()
		{
			outputStream.reset();
		}
	}

	private BufferedOutputStream outputStream;
	private PrintWriter printWriter;

	public BufferedHttpServletResponse(HttpServletResponse response) 
	{
		super(response);
	}

	@Override
	public ServletOutputStream getOutputStream() throws IOException 
	{
		if (printWriter != null)
			throw new IllegalStateException("Cannot call getOutputStream after getWriter");

		if (outputStream == null)
			outputStream = new BufferedOutputStream();
		return outputStream;
	}

	@Override
	public PrintWriter getWriter() throws IOException 
	{
		if(printWriter == null)
		{
			if (outputStream != null)
				throw new IllegalStateException("Cannot call getWriter after getOutputStream");
			
			outputStream = new BufferedOutputStream();
			OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, getCharacterEncoding());
			printWriter = new PrintWriter(outputStreamWriter);
		}
		
		return printWriter;
	}
	
	/**
	 * Returns the output body for this response as a string.
	 * @return the output body for this response. An empty string is returned
	 * if nothing was sent to output. 
	 * @throws IOException if an I/O error occurred while providing the output body.
	 * @since 5.0.0
	 */
	public String getOutputString() throws IOException
	{
		return new String(getOutputByteArray(), getCharacterEncoding());
	}
	
	/**
	 * Returns the output body for this response as a byte array.
	 * @return the output body for this response. An empty array is returned
	 * if nothing was sent to output. 
	 * @throws IOException if an I/O error occurred while providing the output body.
	 * @since 6.0.0
	 */
	public byte[] getOutputByteArray() throws IOException
	{
		if (outputStream != null)
		{
			if(printWriter != null)
				printWriter.flush();
			return outputStream.getOutputStream().toByteArray();
		}
		else
		{
			return new byte[0];
		}
	}
	
	@Override
	public void reset()
	{
		// reset status code in wrapped response
		this.setStatus(SC_OK);
		
		// should reset headers in wrapped response, but can't do it (without doing a full reset)
		
		// reset buffer in this response (but do not touch wrapped response)
		this.resetBuffer();
	}
	
	@Override
	public void resetBuffer()
	{
		// reset buffer in this response (but do not touch wrapped response)
		if (outputStream != null)
		{
			// ensure no bytes pending in the writer pipeline
			if (printWriter != null)
				printWriter.flush();
			
			// clear buffer storage
			outputStream.reset();
		}
	}
}
